# -*- coding:utf-8 -*-

__author__ = 'zhaoxp'

import os
import os.path
import re
import time
import datetime
import traceback
import shutil
import logging
import zipfile

DAY_SECONDS   = 24 * 60 * 60
WEEK_SECONDS  = DAY_SECONDS * 7
MONTH_SECONDS = DAY_SECONDS * 31
YEAR_SECONDS  = DAY_SECONDS * 365


def is_dir_in_dir(dir_path1, dir_path2):
    '''is one directory in another directory
    the two directories should be seperated ones which does not contains the other.
    '''
    dir_path1 = os.path.abspath(dir_path1)
    dir_path2 = os.path.abspath(dir_path2)
    assert os.path.isdir(dir_path1), '%s should be a directory'%dir_path1
    assert os.path.isdir(dir_path2), '%s should be a directory'%dir_path2
    dir_path_parts1 = dir_path1.split(os.path.sep)
    dir_path_parts2 = dir_path2.split(os.path.sep)
    logging.debug('dir_path_parts1 =%s'%dir_path_parts1)
    logging.debug('dir_path_parts2 =%s'%dir_path_parts2)
    min_len = min(len(dir_path_parts1), len(dir_path_parts2))
    index = 0
    while index < min_len:
        if dir_path_parts1[index] != dir_path_parts2[index]:
            return False
        index += 1
    if len(dir_path_parts1) == len(dir_path_parts2):
        return False
    else:
        return True


def match_regexp(the_name, regexp_list):
    for e in regexp_list:
        if re.match(e, the_name):
            return True
    return False


def move_into_directory(src_path, dest_dir_path, classified_dir_name_pattern):
    subdir_name = datetime.datetime.fromtimestamp(os.path.getmtime(src_path)).strftime(classified_dir_name_pattern)
    subdir_full_name = os.path.join(dest_dir_path, subdir_name)
    if os.path.exists(subdir_full_name):
        assert os.path.isdir(subdir_full_name), '%s should be a directory.'%subdir_full_name
        #move file/dir into subdir
    else:
        #create subdir
        os.mkdir(subdir_full_name)
    #shutil.move(src_path, subdir_full_name)
    logging.debug('moving %s into %s'%(src_path, subdir_full_name))


def move_and_classify_files(src_dir_path, dest_dir_path, \
                            classified_dir_name_pattern='%Y-%m-%d', \
                            dir_filter_pattern=['.*'], dir_mtime_older_than=0, \
                            file_filter_pattern=['.*'], file_mtime_older_than=0, \
                            move_subdir=False):
    '''move files and directories of <src_dir_path> into another directory <dest_dir_path>
    and classify these files and directories into different sub-dirs according to <dir_class_pattern>
    
    '''
    logging.debug('move and classify files of %s  into %s'%(src_dir_path, dest_dir_path))
    logging.debug('classified_dir_name_pattern = %s'%(classified_dir_name_pattern))
    logging.debug('dir_filter_pattern          = %s'%(dir_filter_pattern))
    logging.debug('file_filter_pattern         = %s'%(file_filter_pattern))
    logging.debug('move_subdir                 = %s'%(move_subdir))
    assert not is_dir_in_dir(src_dir_path, dest_dir_path), 'src_dir_path and dest_dir_path should not contains each other.'
    assert not os.path.abspath(src_dir_path) == os.path.abspath(dest_dir_path), 'src_dir_path and dest_dir_path should not be same dir'
    for name in os.listdir(src_dir_path):
        full_name = os.path.join(src_dir_path, name)
        if os.path.isdir(full_name):
            if move_subdir and match_regexp(name, dir_filter_pattern):
                mtime = os.path.getmtime(full_name)
                curtime = time.time()
                if curtime - mtime > dir_mtime_older_than:
                    move_into_directory(full_name, dest_dir_path, classified_dir_name_pattern)
        elif os.path.isfile(full_name):
            if match_regexp(name, file_filter_pattern):
                mtime = os.path.getmtime(full_name)
                curtime = time.time()
                if curtime - mtime > file_mtime_older_than:
                    move_into_directory(full_name, dest_dir_path, classified_dir_name_pattern)
        else:
            logging.debug('%s is not directory or file.'%full_name)



def name_maker(full_name):
    ''''make a name for file, the format should be abc.zip, abc_1.zip , abc_2.zip and so on.
    '''
    if not os.path.exists(full_name+'.zip'):
        return full_name+'.zip'
    i = 2
    while True:
        if not os.path.exists('%s_%s.zip'%(full_name, i)):
            return '%s_%s.zip'%(full_name, i)
        i = i + 1


def zip_dir(dir_path, exclude_pattern=[], zipfile_path=None):
    dir_path = os.path.abspath(dir_path)
    logging.debug('zip %s'%dir_path)
    assert os.path.isdir(dir_path), '%s should be a directory'%dir_path
    file_list = []
    zf = None
    if zipfile_path:
        assert not os.path.exists(zipfile_path)
        if zipfile_path[-4:] != '.zip':
            logging.debug('attention: name of file %s is not zip format.'%zipfile_path)
        zf = zipfile.ZipFile(zipfile_path, 'w',zipfile.zlib.DEFLATED, allowZip64=True)
    else:
        zf = zipfile.ZipFile(name_maker(dir_path), 'w',zipfile.zlib.DEFLATED, allowZip64=True)
    try:
        for root, dirs, files in os.walk(dir_path):
            for name in files:
                if not match_regexp(name, exclude_pattern):
                    full_name = os.path.join(root, name)
                    arch_name = full_name[(len(dir_path)-len(os.path.basename(dir_path))-1):]
                    #print arch_name
                    file_list.append(os.path.join(root, name))
                    zf.write(full_name, arch_name)
        return True
    except Exception as ex:
        traceback.print_exc()
        logging.debug('exception during zip_dir: %s'%ex)
        return False
    finally:
        zf.close()


def clean_dir(dir_path, exclude_dir_pattern=[], dir_mtime_older_than=0, exclude_file_pattern=[], file_mtime_older_than=0):
    '''delete directories and files in <dir_path> according to exclude dir/file pattern and modified time
    '''
    logging.debug('start to clean subdirs or files in dir %s'%dir_path)
    dir_path = os.path.abspath(dir_path)
    assert os.path.isdir(dir_path), '%s should be a directory'%dir_path
    for name in os.listdir(dir_path):
        full_name = os.path.join(dir_path, name)
        if os.path.isdir(full_name):
            if not match_regexp(name, exclude_dir_pattern):
                mtime = os.path.getmtime(full_name)
                curtime = time.time()
                if curtime - mtime > dir_mtime_older_than:
                    shutil.rmtree(full_name)
                    logging.debug('delete directory %s'%full_name)
        elif os.path.isfile(full_name):
            if not match_regexp(name, exclude_file_pattern):
                mtime = os.path.getmtime(full_name)
                curtime = time.time()
                if curtime - mtime > file_mtime_older_than:
                    os.remove(full_name)
                    logging.debug('delete file %s'%full_name)
        else:
            logging.debug('%s is not directory or file.'%full_name)


def clean_dir_by_timed_name(dir_path, time_pattern='%Y-%m-%d', name_range_index=[0, 10], older_than=DAY_SECONDS*3):
    logging.debug('clean dir by timed name : %s'%dir_path)
    logging.debug('time_pattern = %s and older_than = %s'%(time_pattern, older_than))
    dir_path = os.path.abspath(dir_path)
    assert os.path.isdir(dir_path), '%s should be a directory'%dir_path
    curtime = time.time()
    for name in os.listdir(dir_path)    :
        full_name = os.path.join(dir_path, name)
        name_time_str = name[name_range_index[0]:name_range_index[1]]
        try:
            dt = datetime.datetime.strptime(name_time_str, time_pattern)
            if curtime - time.mktime(dt.timetuple()) > older_than:
                if os.path.isdir(full_name):
                    shutil.rmtree(full_name)
                    logging.debug('delete directory %s'%full_name)
                else:
                    os.remove(full_name)
                    logging.debug('delete file %s'%full_name)
        except ValueError as ve:
            pass


def process_codebase_backup_dir(dir_path):
    '''process codebase backup directory in 201.86
    '''
    logging.debug('process codebase backup dir %s'%dir_path)
    #D:\code\codebase_backup\
    dir_path = os.path.abspath(dir_path)
    curtime = time.time()
    for sys_name in os.listdir(dir_path):
        logging.debug('processing system %s'%sys_name)
        new_dir_path = os.path.join(dir_path, sys_name, 'upload')
        if os.path.isdir(new_dir_path):
            for action_name in os.listdir(new_dir_path):
                # action_name : EBG_PRE_20161113-231528, TBG_PRE_20170309-191843, ELOIS_QAS_20161121-171825
                full_name = os.path.join(new_dir_path, action_name)
                time_str = action_name[len(sys_name)+1:len(sys_name)+9]
                try:
                    dt = datetime.datetime.strptime(time_str, '%Y%m%d')
                    action_time = time.mktime(dt.timetuple())
                    if os.path.isdir(full_name):
                        if curtime - action_time > DAY_SECONDS:
                            logging.debug('  zipping       %s'%full_name)
                            if zip_dir(full_name):
                                logging.debug('  deleting dir  %s'%full_name)
                                shutil.rmtree(full_name)
                            else:
                                logging.debug('  failed to delete dir  %s'%full_name)
                    else:
                        if curtime - action_time > YEAR_SECONDS:
                            logging.debug('  deleting file %s'%full_name)
                            os.remove(full_name)
                except BaseException as be:
                    traceback.print_exc()
                    logging.debug('exception: %s %s'%(full_name, be))


def zip_subdirs(dir_path):
    logging.debug('zip all sub dirs of %s ...'%dir_path)
    dir_path = os.path.abspath(dir_path)
    assert os.path.isdir(dir_path), '%s should be a directory'%dir_path
    for name in os.listdir(dir_path):
        full_name = os.path.join(dir_path, name)
        if name[0:2] == '20' and len(name) > 4 and os.path.isdir(full_name):
            #logging.debug('zip %s'%full_name)
            if zip_dir(full_name):
                logging.debug('del %s'%full_name)
                shutil.rmtree(full_name)


def calc_dir_size(dir_path):
    dir_path = os.path.abspath(dir_path)
    assert os.path.isdir(dir_path), '%s should be a directory'%dir_path
    size = 0
    for name in os.listdir(dir_path):
        full_name = os.path.join(dir_path, name)
        if os.path.isdir(full_name):
            size += calc_dir_size(full_name)
        elif os.path.isfile(full_name):
            size += os.path.getsize(full_name)
        else:
            logging.debug('calc_dir_size : unrecognised file %s'%full_name)
    return size


def calc_subdir_size(dir_path, include_file=False, show_size_than=0, size_format=True):
    dir_path = os.path.abspath(dir_path)
    assert os.path.isdir(dir_path), '%s should be a directory'%dir_path
    size, all_size = 0, 0
    for name in os.listdir(dir_path):
        full_name = os.path.join(dir_path, name)
        if os.path.isdir(full_name):
            size = calc_dir_size(full_name)
            all_size += size
            if size >= show_size_than:
                if size_format:
                    print('dir  %10s %s'%(size_formater(size,''), full_name))
                else:
                    print('dir  %15s %s'%(size_separator(size), full_name))
        elif os.path.isfile(full_name):
            size = os.path.getsize(full_name)
            all_size += size
            if include_file:
                if size >= show_size_than:
                    if size_format:
                        print('file %10s %s'%(size_formater(size, ''), full_name))
                    else:
                        print('file %15s %s'%(size_separator(size), full_name))
        else:
            print('calc_subdir_size : unrecognised file %s'%full_name)
    print('all size = %s or detailed %s  %s'%(size_formater(all_size), size_separator(all_size), dir_path))
    '''
    if size_format:
        print('all size = %s %s'%(size_formater(all_size), dir_path))
    else:
        print('all size = %s %s'%(size_separator(all_size), dir_path))
    '''


def size_formater(size, sep=''):
    if size < 1024:
        return '%s%s B'%(size, sep)
    elif size < 1024 * 1024:
        return '%s%sKB'%(size / 1024, sep)
    elif size < 1024 * 1024 * 1024:
        return '%s%sMB'%(size / (1024 * 1024), sep)
    elif size < 1024 * 1024 * 1024 * 1024:
        return '%s%sGB'%(size / (1024 * 1024 * 1024), sep)
    elif size < 1024 * 1024 * 1024 * 1024 * 1024:
        return '%s%sTB'%(size / (1024 * 1024 * 1024 * 1024), sep)
    else:
        return '%s%sPB'%(size / (1024 * 1024 * 1024 * 1024 * 1024), sep)


def size_separator(size, sep=','):
    size_str = str(size)
    size_len = len(size_str)
    remainder = size_len % 3
    new_size_str = ''
    index = 0
    if remainder:
        new_size_str += size_str[:remainder]
        index += remainder
    while index < size_len:
        if index > 0:
            new_size_str += ',' + size_str[index:index+3]
        else:
            new_size_str += size_str[index:index+3]
        index += 3
    return new_size_str

if __name__=='__main__':
    logging.basicConfig(level=logging.DEBUG)
    logging.info('start to process files')
    #move_and_classify_files('D:\\temp\\py', 'D:\\temp\\py_test', file_filter_pattern=['.*.py$'])
    #zip_dir('D:\\temp\\py')
    #clean_dir('D:\\temp\\py')
    #D:\\code\\codebase_backup
    dir_path = 'D:\\code\\code_production\\TBG_Production\\2016'
    #process_codebase_backup_dir(dir_path)
    #zip_subdirs(dir_path)
    calc_subdir_size('d:\\BASIS\\zhaoxp', include_file=True, size_format=False)
    #calc_subdir_size('d:\\temp\\py', include_file=True, size_format=False)
